﻿using Peer.Define;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;

namespace Peer
{
    public static class PeerExtensions
    {
        public static Task SendAnyAsync(this PeerStream peer, params object[] args)
        {
            var argList = args.Select(peer.converter.Serialize).ToList();
            return peer.SendAsync(peer.converter.Serialize(argList));
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="peer"></param>
        /// <param name="bytes">字节数组</param>
        /// <returns></returns>
        public static Task SendAsync(this PeerStream peer, byte[] bytes)
        {
            return peer.SendAsync(new MemoryStream(bytes));
        }

        /// <summary>
        /// 发送并响应数据
        /// </summary>
        /// <param name="peer"></param>
        /// <param name="bytes">字节数组</param>
        /// <returns></returns>
        public static async Task<T> SendAnyWithResponseAsync<T>(this PeerStream peer, params object[] args)
        {
            var argList = args.Select(peer.converter.Serialize).ToList();
            var response = await peer.SendWithResponseAsync(peer.converter.Serialize(argList));
            return peer.converter.Deserialize<T>(response);
        }

        /// <summary>
        /// 发送并响应数据
        /// </summary>
        /// <param name="peer"></param>
        /// <param name="bytes">字节数组</param>
        /// <returns></returns>
        public static async Task<byte[]> SendWithResponseAsync(this PeerStream peer, byte[] bytes)
        {
            var response = await peer.SendWithResponseAsync(new MemoryStream(bytes));

            return response.GetBytes();
        }


        public static Parameters GetParameters(this PeerRequest request)
        {
            return new Parameters(request.converter, StreamToBytes(request.Data));
        }

        public static Stream GetStream(this PeerResponse response)
        {
            response.Data.Position = 0;
            if (response.PackageType == PackageType.ResponseError)
            {
                throw (Exception)response.converter.DeserializeObject(response.Data);
            }
            return response.Data;
        }


        public static byte[] GetBytes(this PeerResponse response)
        {
            if (response.PackageType == PackageType.Response || response.Data == null)
            {
                return new byte[0];
            }
            using var stream = response.GetStream();
            return StreamToBytes(stream);
        }
        /// <summary>
        /// 可系列化对象
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <param name="request"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static PeerResponse CreateResponse<S>(this PeerRequest request, S obj) where S : ISerializable
        {
            return request.CreateResponse(request.converter.Serialize(obj));
        }

        /// <summary>
        /// 任意对象
        /// </summary>
        /// <param name="request"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static PeerResponse CreateResponse(this PeerRequest request, object obj)
        {
            return request.CreateResponse(request.converter.Serialize(obj));
        }

        /// <summary>
        /// 创建字节数组
        /// </summary>
        /// <param name="request"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static PeerResponse CreateResponse(this PeerRequest request, byte[] bytes)
        {
            return request.CreateResponse(new MemoryStream(bytes));
        }

        static byte[] StreamToBytes(Stream stream)
        {
            if (stream is MemoryStream memoryStream)
            {
                return memoryStream.ToArray();
            }
            stream.Position = 0;
            using var ms = new MemoryStream();
            stream.CopyTo(ms);
            return ms.ToArray();
        }
    }
}
